﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace bianyi
{
    internal class Nyx
    {
    }

    public enum ValueType { Int, Double, String, Bool, Char, Null, Array };

    public enum ExecutionResultType { ExecNormal, ExecReturn, ExecBreak, ExecContinue };

    public delegate Value BuiltinFuncType(Runtime rt, List<Context> contexts, List<Value> values);

    public class Runtime : Context
    {
        private Dictionary<string, BuiltinFuncType> builtin;
        private List<Statement> stmts = new List<Statement>();

        internal List<Statement> getStatements()
        {
            return stmts;
        }

        //Dictionary<string, BuiltinFuncType>()

        public Runtime()
        {
            builtin = new Dictionary<string, BuiltinFuncType>();

            builtin["print"] = new BuiltinFuncType(Builtin.nyx_builtin_print);
            builtin["createVmFromDb"] = new BuiltinFuncType(Builtin.nyx_builtin_createVmFromDb);
            builtin["checkConnect"] = new BuiltinFuncType(Builtin.nyx_builtin_checkConnect);
            builtin["getTables"] = new BuiltinFuncType(Builtin.nyx_builtin_getTables);
            builtin["createVmFromJsonFile"] = new BuiltinFuncType(Builtin.nyx_builtin_createVmFromJsonFile);

            builtin["println"] = new BuiltinFuncType(Builtin.nyx_builtin_println);
            builtin["typeof"] = new BuiltinFuncType(Builtin.nyx_builtin_typeof);
            builtin["input"] = new BuiltinFuncType(Builtin.nyx_builtin_input);
            builtin["length"] = new BuiltinFuncType(Builtin.nyx_builtin_length);
            //builtin["helloworld"] = new BuiltinFuncType(Builtin.nyx_builtin_helloworld);
        }

        internal void addFunction(object name, object f)
        {
            throw new NotImplementedException();
        }

        internal void addStatementt(Statement statement)
        {
            stmts.Add(statement);
        }

        public BuiltinFuncType getBuiltinFunction(string name)
        {
            var res = builtin.FirstOrDefault(m => m.Key == name);
            if (res.Key != null)
            {
                return res.Value;
            }
            return res.Value;
        }
    }

    public class Function
    {
        public string name;
        private List<string> listparams;
        private Block block;
        private Expression retExpr;
    };

    public class Block
    {
        public List<Statement> stmts;
    };

    public class Value
    {
        public Value()
        {
        }

        public Value(ValueType type, object data)
        {
            this.type = type;
            this.data = data;
        }

        public ValueType type;
        public object data;

        public static Value operator +(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Array)
            {
                result.type = ValueType.Array;
                result.data = (lhs.data as ArrayList).Add(rhs.data as ArrayList);
            }
            else if (lhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = (int)lhs.data + (int)rhs.data;
            }
            else if (lhs.type == ValueType.String)
            {
                result.type = lhs.type;
                result.data = (string)lhs.data + (string)rhs.data;
            }
            else if (lhs.type == ValueType.Double)
            {
                result.type = lhs.type;
                result.data = (double)lhs.data + (double)rhs.data;
            }
            else
            { Console.WriteLine("TypeError: unexpected arguments of operator +"); }
            return result;
        }

        public static Value operator -(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Array)
            {
                result.type = ValueType.Array;
                result.data = (lhs.data as ArrayList).Add(rhs.data as ArrayList);
            }
            else if (lhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = (int)lhs.data + (int)rhs.data;
            }
            else if (lhs.type == ValueType.String)
            {
                result.type = lhs.type;
                result.data = (string)lhs.data + (string)rhs.data;
            }
            else if (lhs.type == ValueType.Double)
            {
                result.type = lhs.type;
                result.data = (double)lhs.data + (double)rhs.data;
            }
            else
            { Console.WriteLine("TypeError: unexpected arguments of operator -"); }
            return result;
        }

        public static Value operator *(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Array && rhs.type == ValueType.Int)
            {
                result.type = ValueType.Array;
                result.data = (lhs.data as List<object>).Append(lhs.data as List<object>);
            }
            else if (lhs.type == ValueType.Int && rhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = (int)lhs.data * (int)rhs.data;
            }
            else if (lhs.type == ValueType.String && rhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = Utils.repeatString((int)rhs.data, (string)lhs.data);
            }
            else if (lhs.type == ValueType.Double && rhs.type == ValueType.Double)
            {
                result.type = lhs.type;
                result.data = (double)lhs.data * (double)rhs.data;
            }
            else
            {
                Console.WriteLine("TypeError: unexpected arguments of operator *");
            }
            return result;
        }

        public static Value operator /(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Array)
            {
                result.type = ValueType.Array;
                result.data = (lhs.data as ArrayList).Add(rhs.data as ArrayList);
            }
            else if (lhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = (int)lhs.data + (int)rhs.data;
            }
            else if (lhs.type == ValueType.String)
            {
                result.type = lhs.type;
                result.data = (string)lhs.data + (string)rhs.data;
            }
            else if (lhs.type == ValueType.Double)
            {
                result.type = lhs.type;
                result.data = (double)lhs.data + (double)rhs.data;
            }
            else
            { Console.WriteLine("TypeError: unexpected arguments of operator /"); }
            return result;
        }

        public static Value operator %(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Array)
            {
                result.type = ValueType.Array;
                result.data = (lhs.data as ArrayList).Add(rhs.data as ArrayList);
            }
            else if (lhs.type == ValueType.Int)
            {
                result.type = lhs.type;
                result.data = (int)lhs.data + (int)rhs.data;
            }
            else if (lhs.type == ValueType.String)
            {
                result.type = lhs.type;
                result.data = (string)lhs.data + (string)rhs.data;
            }
            else if (lhs.type == ValueType.Double)
            {
                result.type = lhs.type;
                result.data = (double)lhs.data + (double)rhs.data;
            }
            else
            { Console.WriteLine("TypeError: unexpected arguments of operator %"); }
            return result;
        }

        public static Value operator >(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Int && rhs.type == ValueType.Int)
            {
                result.type = ValueType.Bool;
                result.data = (int)lhs.data > (int)rhs.data;
            }
            else if (lhs.type == ValueType.Double && rhs.type == ValueType.Double)
            {
                result.type = ValueType.Bool;
                result.data = (Double)lhs.data > (Double)rhs.data;
            }
            else if (lhs.type == ValueType.String && rhs.type == ValueType.String)
            {
                result.type = ValueType.Bool;
                result.data = (lhs.data.ToString().CompareTo(rhs.data.ToString())) > 0;
            }
            else
            {
                Console.WriteLine("TypeError: unexpected arguments of operator >");
            }
            return result;
        }

        public static Value operator <(Value lhs, Value rhs)
        {
            Value result = new Value();
            if (lhs.type == ValueType.Int && rhs.type == ValueType.Int)
            {
                result.type = ValueType.Bool;
                result.data = (int)lhs.data < (int)rhs.data;
            }
            else if (lhs.type == ValueType.Double && rhs.type == ValueType.Double)
            {
                result.type = ValueType.Bool;
                result.data = (Double)lhs.data < (Double)rhs.data;
            }
            else if (lhs.type == ValueType.String && rhs.type == ValueType.String)
            {
                result.type = ValueType.Bool;
                result.data = (lhs.data.ToString().CompareTo(rhs.data.ToString())) < 0;
            }
            else
            {
                Console.WriteLine("TypeError: unexpected arguments of operator <");
            }
            return result;
        }

        public static Value operator >=(Value lhs, Value rhs)
        {
            Value result = new Value();

            return result;
        }

        public static Value operator <=(Value lhs, Value rhs)
        {
            Value result = new Value();

            return result;
        }

        public static Value operator |(Value lhs, Value rhs)
        {
            Value result = new Value();

            return result;
        }

        //public static Value operator ||(Value lhs, Value rhs)
        //{
        //    Value result = new Value();

        //    return result;
        //}
        public static Value operator &(Value lhs, Value rhs)
        {
            Value result = new Value();

            return result;
        }

        //public static Value operator &&(Value lhs, Value rhs)
        //{
        //    Value result = new Value();

        //    return result;
        //}
    }

    public class Variable
    {
        public string name;
        public Value value;
    }

    public class Context
    {
        private Dictionary<string, Variable> vars = new Dictionary<string, Variable>();
        private Dictionary<string, Function> funcs = new Dictionary<string, Function>();

        public bool hasVariable(string identName)
        {
            string vars = null;
            return vars.Contains(identName);
        }

        public void createVariable(string identName, Value value)
        {
            var var = new Variable();
            var.name = identName;
            var.value = value;
            vars.Add(identName, var);
        }

        public Variable getVariable(string identName)
        {
            if (vars != null && vars.Count > 0)
            {
                var res = vars.FirstOrDefault(m => m.Key == identName);
                return res.Value;
            }
            else { return null; }
        }

        public void addFunction(string name, Function f)
        {
            funcs.Add(name, f);
        }

        public bool hasFunction(string name)
        {
            return funcs.ContainsKey(name);
        }

        public Function getFunction(string name)
        {
            return funcs.FirstOrDefault(m => m.Key == name).Value;
        }
    }
}